package com.iceland.common.rpc;

import com.iceland.common.message.CmdMessage;
import com.iceland.common.message.MessageType;
import com.iceland.common.strategy.CmdStrategy;
import com.iceland.common.thread.MessageEventExcutor;
import com.iceland.common.util.AttributeKeyUtils;
import com.iceland.common.util.ChannelContext;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 处理远程调用请求
 */
@Slf4j
public class MethodInvocationStrategy implements CmdStrategy {

    @Override
    public void doStrategy(ChannelHandlerContext ctx, CmdMessage m) {
        MessageEventExcutor.getInstance().submit(new Runnable() {
            @Override
            public void run() {
                try {
                    doMethodInvocationStrategy(ctx,m);
                }catch (Throwable e){
                    log.error("MethodInvocationResponseStrategy执行失败",e);
                }
            }
        });
    }
    public void doMethodInvocationStrategy(ChannelHandlerContext ctx, CmdMessage m) {
        log.debug("本地方法被远程调用,remoteAddress:{},localAddress:{}",ctx.channel().remoteAddress(),ctx.channel().localAddress());
        ChannelContext channelContext = AttributeKeyUtils.getChannelContext(ctx.channel());
        //将rpcMessageManager放入本地线程，方便调用服务时获取
        RpcMessageManager rpcMessageManager = channelContext.getRpcMessageManager();
        RpcMessageManagerHolder.setRpcMessageManager(rpcMessageManager);
        //创建响应消息
        CmdMessage resposeMessage=new CmdMessage(MessageType.METHOD_INVOKE_RESULT.getType());
        try {
            RpcRequest request = m.getData();
            RpcResponse response=new RpcResponse();
            response.setRequestId(request.getRequestId());

            //调用本地服务获取返回结果
            Object bean = RpcServiceClassFactory.getBean(request.getPath());
            Class beanClass=bean.getClass();
            try {
                Object[] parameters = request.getParameters();
                Class[] parametersType=new Class[parameters.length];
                for (int i = 0; i < parametersType.length; i++) {
                    parametersType[i]=parameters[i].getClass();
                }
                Method method = beanClass.getMethod(request.getMethod(), parametersType);
                Object returnValue = method.invoke(bean, request.getParameters());
                response.setReturnValue(returnValue);
            }
            catch (InvocationTargetException e) {
                Throwable t = e .getTargetException();
                log.error("本地方法被远程调用,发生错误",t);
                //服务调用失败，返回响应异常
                response.setRpcStatus(RpcStatusEnum.ERROR);
                response.setException(t.getMessage());
                resposeMessage.setData(response);
                ctx.writeAndFlush(resposeMessage);
                return;
                //写入日志
            }catch (Exception e) {
                log.error("本地方法被远程调用,发生错误",e);
                //服务调用失败，返回响应异常
                response.setRpcStatus(RpcStatusEnum.ERROR);
                response.setException(e.getMessage());
                resposeMessage.setData(response);
                ctx.writeAndFlush(resposeMessage);
                return;
            }

            log.debug("本地方法被远程调用,调用成功,remoteAddress:{},localAddress:{}",ctx.channel().remoteAddress(),ctx.channel().localAddress());
            //将返回结果写入响应
            response.setRpcStatus(RpcStatusEnum.SUCCESS);
            resposeMessage.setData(response);
        }finally {
            //清理线程变量
            RpcMessageManagerHolder.clear();
        }

        ctx.channel().writeAndFlush(resposeMessage);
    }

    @Override
    public int getType() {
        return MessageType.METHOD_INVOKE.getType();
    }
}
